import 'dart:async';
import 'dart:math';

import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';
import 'package:sonora/models/sj_medal_data.dart';
import 'package:sonora/models/sj_media_data.dart';
import 'package:video_player/video_player.dart';

typedef SjLoadMoreVideo = Future<List<SjVpVideoController>> Function(
    int index, List<SjVpVideoController> list);

class SjVideoListController extends ChangeNotifier {
  SjVideoListController(
      {this.loadMoreCount = 1, this.preloadCount = 2, this.disposeCount = 0});
  final int loadMoreCount;
  final int preloadCount;
  final int disposeCount;
  SjLoadMoreVideo? _videoProvider;
  bool changePage = false;

  loadIndex(int target, {bool reload = false, bool play = true}) {
    if (!reload) {
      if (index.value == target) return;
    }
    var oldIndex = index.value;
    var newIndex = target;

    if (!(oldIndex == 0 && newIndex == 0)) {
      playerOfIndex(oldIndex)?.controller?.seekTo(Duration.zero);
      playerOfIndex(oldIndex)?.pause();
    }
    playerOfIndex(newIndex)?.controller?.addListener(_didUpdateValue);
    playerOfIndex(newIndex)?.showPauseIcon.addListener(_didUpdateValue);
    if (play) {
      playerOfIndex(newIndex)?.play();
    } else {
      playerOfIndex(newIndex)?.pause();
    }

    for (var i = 0; i < playerList.length; i++) {
      if (i < newIndex - disposeCount || i > newIndex + max(disposeCount, 2)) {
        playerOfIndex(i)?.controller?.pause();
        playerOfIndex(i)?.controller?.removeListener(_didUpdateValue);
        playerOfIndex(i)?.showPauseIcon.removeListener(_didUpdateValue);
        // playerOfIndex(i)?.dispose();
        continue;
      }
      // if (i > newIndex && i < newIndex + preloadCount) {
      //   playerOfIndex(i)?.init();
      //   continue;
      // }
    }
    // if (playerList.length - newIndex <= loadMoreCount + 1) {
    //   _videoProvider?.call(newIndex, playerList).then((list) async {
    //     playerList.addAll(list);
    //     notifyListeners();
    //   });
    // }
    index.value = target;
    changePage = false;
  }

  _didUpdateValue() {
    notifyListeners();
  }

  SjVpVideoController? playerOfIndex(int index) {
    if (index < 0 || index > playerList.length - 1) {
      return null;
    }
    return playerList[index];
  }

  int get videoCount => playerList.length;

  init({
    required PageController pageController,
    required List<SjVpVideoController> initialList,
    required SjLoadMoreVideo videoProvider,
  }) async {
    playerList.clear();
    playerList.addAll(initialList);
    _videoProvider = videoProvider;
    pageController.addListener(() {
      var p = pageController.page!;
      if (p % 1 == 0) {
        loadIndex(p ~/ 1, play: !changePage);
      }
    });
    // loadIndex(0, reload: true);
    notifyListeners();
  }

  reset({
    required PageController pageController,
    required List<SjVpVideoController> initialList,
    required SjLoadMoreVideo videoProvider,
    required int currentPage,
  }) async {
    playerList.removeAt(currentPage);
    int index = currentPage;
    if (playerList.length == currentPage) {
      index = currentPage - 1;
    }
    if (this.index.value > currentPage) {
      this.index.value = this.index.value - 1;
    }
    if (playerList.isNotEmpty) {
      loadIndex(index, reload: true);
    } else {
    }
    notifyListeners();
  }

    reload({
    required PageController pageController,
    required List<SjVpVideoController> initialList,
    required SjLoadMoreVideo videoProvider,
  }) async {
    for (var player in playerList) {
      await player.pause();
      player.showPauseIcon.value = false;
    }
    playerList.clear();
    playerList.addAll(initialList);
    _videoProvider = videoProvider;
    loadIndex(pageController.page!.toInt(), reload: true, play: false);
    notifyListeners();
  }

  ValueNotifier<int> index = ValueNotifier<int>(0);

  List<SjVpVideoController> playerList = [];
  SjVpVideoController get currentPlayer => playerList[index.value];

  @override
  void dispose() {
    print('----------------------SjVideoListController dispose');
    for (var player in playerList) {
      player.showPauseIcon.dispose();
      player.dispose();
    }
    playerList = [];
    super.dispose();
  }

void clearVideoPlayer() {
  print('----------------------SjVideoListController destroy');
    for (var player in playerList) {
      player.showPauseIcon.dispose();
      player.dispose();
    }
    playerList = [];
}
}

typedef SjControllerSetter<T> = Future<void> Function(T controller);
typedef SjControllerBuilder<T> = T Function();

abstract class SjVideoController<T> {
  T? get controller;
  ValueNotifier<bool> get showPauseIcon;
  Future<void> init({SjControllerSetter<T>? afterInit});
  Future<void> dispose();
  Future<void> play();
  Future<void> pause({bool showPauseIcon = false});
}

Completer<void>? _syncLock;

class SjVpVideoController extends SjVideoController<VideoPlayerController> {
  VideoPlayerController? _controller;
  ValueNotifier<bool> _showPauseIcon = ValueNotifier<bool>(false);
  final SjMediaData? videoInfo;
  final SjControllerBuilder<VideoPlayerController> _builder;
  final SjControllerSetter<VideoPlayerController>? _afterInit;

  SjVpVideoController({
    this.videoInfo,
    required SjControllerBuilder<VideoPlayerController> builder,
    SjControllerSetter<VideoPlayerController>? afterInit,
  })  : _builder = builder,
        _afterInit = afterInit;

  @override
  VideoPlayerController? get controller {
    _controller ??= _builder.call();
    return _controller;
  }

  bool get isDispose => _disposeLock != null;
  bool get prepared => _prepared;
  bool _prepared = false;

  Completer<void>? _disposeLock;

  Future<void> _syncCall(Future Function()? fn) async {
    var lastCompleter = _syncLock;
    var completer = Completer<void>();
    _syncLock = completer;
    await lastCompleter?.future;
    await fn?.call();
    completer.complete();
  }

  @override
  Future<void> dispose() async {
    print('----------------------SjVpVideoController dispose');
    if (!prepared) return;
    _prepared = false;
    await _syncCall(() async {
      if (controller != null) {
        controller?.pause();
        controller?.seekTo(Duration.zero);
        _controller = null;
      }
      _disposeLock = Completer<void>();
    });
  }

  @override
  Future<void> init(
      {SjControllerSetter<VideoPlayerController>? afterInit}) async {
    if (prepared) return;
    await _syncCall(() async {
      await controller?.initialize();
      await controller?.setLooping(true);
      afterInit ??= _afterInit;
      await afterInit?.call(controller!);
      _prepared = true;
    });
    if (_disposeLock != null) {
      print('----------------------SjVpVideoController init _disposeLock');
      _disposeLock?.complete();
      _disposeLock = null;
    }
  }

  @override
  Future<void> pause({bool showPauseIcon = false}) async {
    print('----------------------SjVpVideoController pause');
    await init();
    if (!prepared) return;
    if (_disposeLock != null) {
      print('----------------------SjVpVideoController pause _disposeLock');
      await _disposeLock?.future;
    }
    await controller?.pause();
    _showPauseIcon.value = true;
  }

  @override
  Future<void> play() async {
      print('----------------------SjVpVideoController play');
    await init();
    if (!prepared) return;
    if (_disposeLock != null) {
      print('----------------------SjVpVideoController play _disposeLock');
      await _disposeLock?.future;
    }
    await controller?.play();
    _showPauseIcon.value = false;
  }

  @override
  ValueNotifier<bool> get showPauseIcon => _showPauseIcon;
}
